package cn.redragon.soa.common.brand;

import static cn.redragon.soa.common.brand.BrandError.APP_CODE_NOT_SUPPORT;
import static cn.redragon.soa.common.brand.BrandError.BRAND_NOT_SUPPORT;
import static cn.redragon.soa.common.brand.BrandError.EMPLOYEE_TYPE_CODE_NOT_SUPPORT;
import static cn.redragon.soa.common.brand.BrandError.OWNER_ID_NOT_SUPPORT;
import static cn.redragon.soa.common.brand.BrandError.PIM_CHANNEL_NOT_SUPPORT;
import static cn.redragon.soa.common.brand.BrandError.WW_AGENT_CODE_NOT_SUPPORT;

import io.vavr.control.Try;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "soa.common")
public class BrandUtil {

    @Setter
    private Map<Brand, BrandProperty> brands = new HashMap<>();

    public BrandProperty getByBrandCode(String brandCode) {
        return this.getByBrand(
            Try.of(() -> Brand.valueOf(brandCode)).getOrElseThrow(() -> new IllegalArgumentException(BRAND_NOT_SUPPORT.withParams(brandCode))));
    }

    public BrandProperty getByBrand(Brand brand) {
        return Optional.ofNullable(brands.get(brand)).orElseThrow(
            () -> new IllegalArgumentException(BRAND_NOT_SUPPORT.withParams(brand.name())));
    }

    public BrandProperty getByAppCode(String appCode) {
        return brands.values().stream().filter(bp -> bp.getAppCode().equalsIgnoreCase(appCode)).findFirst().orElseThrow(
            () -> new IllegalArgumentException(APP_CODE_NOT_SUPPORT.withParams(appCode)));
    }

    public BrandProperty getByOwnerId(String ownerId) {
        return brands.values().stream().filter(bp -> bp.getOwnerId().equalsIgnoreCase(ownerId)).findFirst().orElseThrow(
            () -> new IllegalArgumentException(OWNER_ID_NOT_SUPPORT.withParams(ownerId)));
    }

    public BrandProperty getByPimChannel(String pimChannel) {
        return brands.values().stream().filter(bp -> bp.getPimChannel().equalsIgnoreCase(pimChannel)).findFirst().orElseThrow(
            () -> new IllegalArgumentException(PIM_CHANNEL_NOT_SUPPORT.withParams(pimChannel)));
    }

    public List<BrandProperty.MemberType> getMemberTypes(String brandCode) {
        return this.getByBrandCode(brandCode).getMemberTypes();
    }

    public List<BrandProperty.EmployeeType> getEmployeeTypes(String brandCode) {
        return this.getByBrandCode(brandCode).getEmployeeTypes();
    }

    public List<BrandProperty.WwAgent> getWwAgents(String brandCode) {
        return this.getByBrandCode(brandCode).getWwAgents();
    }

    public BrandProperty.MemberType getFirstLevelMemberType(String brandCode) {
        return getMemberTypesWithDefaultList(brandCode).get(0);
    }

    public BrandProperty.MemberType getMemberTypeWithDefaultName(String brandCode, String code, String defaultName) {
        return getMemberTypesWithDefaultList(brandCode).stream().filter(i -> i.getCode().equals(code)).findFirst().orElse(
            new BrandProperty.MemberType(code, defaultName));
    }


    public BrandProperty.MemberType getMemberTypeByCrmCode(String brandCode, String crmCode) {
        List<BrandProperty.MemberType> types = getMemberTypes(brandCode);
        if (CollectionUtils.isEmpty(types)) {
            return null;
        }

        return types.stream()
            .filter(memberType -> StringUtils.equals(crmCode, memberType.getCrmCode()))
            .findFirst()
            .orElse(null);
    }

    public BrandProperty.MemberType getMemberTypeByTagId(String brandCode, String tagId) {
        List<BrandProperty.MemberType> types = getMemberTypes(brandCode);
        if (CollectionUtils.isEmpty(types)) {
            return null;
        }

        return types.stream()
            .filter(memberType -> StringUtils.equals(tagId, memberType.getTagId()))
            .findFirst()
            .orElse(null);
    }

    public BrandProperty.EmployeeType getEmployeeType(String brandCode, String code) {
        return getEmployeeTypes(brandCode).stream().filter(i -> i.getCode().equals(code)).findFirst().orElseThrow(
            () -> new IllegalArgumentException(EMPLOYEE_TYPE_CODE_NOT_SUPPORT.withParams(code)));
    }

    public BrandProperty.WwAgent getWwAgent(String brandCode, String code) {
        return getWwAgents(brandCode).stream().filter(i -> i.getCode().equals(code)).findFirst().orElseThrow(
            () -> new IllegalArgumentException(WW_AGENT_CODE_NOT_SUPPORT.withParams(code)));
    }

    public boolean brandExists(String brandCode) {
        return brands.containsKey(
            Try.of(() -> Brand.valueOf(brandCode)).getOrElseThrow(() -> new IllegalArgumentException(BRAND_NOT_SUPPORT.withParams(brandCode))));
    }

    public boolean brandExists(Brand brand) {
        return brands.containsKey(brand);
    }

    public boolean appExists(String appCode) {
        return brands.values().stream().anyMatch(bp -> bp.getAppCode().equalsIgnoreCase(appCode));
    }

    public boolean ownerIdExists(String ownerId) {
        return brands.values().stream().anyMatch(bp -> bp.getOwnerId().equalsIgnoreCase(ownerId));
    }

    public boolean pimChannelExists(String pimChannel) {
        return brands.values().stream().anyMatch(bp -> bp.getPimChannel().equalsIgnoreCase(pimChannel));
    }

    public boolean crmIsShuYun(Brand brand) {
        return BooleanUtils.isTrue(this.getByBrand(brand).crmIsShuYun);
    }

    public boolean crmIsShuYun(String brandCode) {
        return BooleanUtils.isTrue(this.getByBrandCode(brandCode).crmIsShuYun);
    }

    public String getWxMiniShopId(String brandCode) {
        return this.getByBrandCode(brandCode).getWxMiniShopId();
    }

    private List<BrandProperty.MemberType> getMemberTypesWithDefaultList(String brandCode) {
        return Optional.ofNullable(getMemberTypes(brandCode)).orElse(List.of(new BrandProperty.MemberType("1", "潜在会员")));
    }

    @Data
    public static class BrandProperty {

        private Brand code;
        private Brand swpCode;
        private String appCode;
        private String appId;
        private Boolean crmIsShuYun;
        private String ownerId;
        private String crmShopCode;
        private String pimChannel;
        private String wxMiniShopId;
        private List<String> roles;
        private List<MemberType> memberTypes;
        private List<EmployeeType> employeeTypes;
        private List<WwAgent> wwAgents;

        @Data
        @AllArgsConstructor(access = AccessLevel.PRIVATE)
        @NoArgsConstructor
        public static class MemberType {

            private String code;
            private String name;
            private String tagId;
            private String crmCode;
            private String thirdPartyName;
            private String exp;

            private MemberType(String code, String name) {
                this.code = code;
                this.name = name;
            }
        }

        @Data
        @AllArgsConstructor(access = AccessLevel.PRIVATE)
        @NoArgsConstructor
        public static class EmployeeType {

            private String code;
            private String name;
        }

        @Data
        @AllArgsConstructor(access = AccessLevel.PRIVATE)
        @NoArgsConstructor
        public static class WwAgent {

            private String code;
            private String agentId;
        }
    }
}
